﻿/*
    Microformats Test Runner 0.1
    Author: Glenn Jones
    
	The MIT License
	
	Copyright (c) 2008 Glenn Jones
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:
	
	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.
	
	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.
	
*/


function UtilitiesBase() {
    var me;
    if (this.constructor == UtilitiesBase){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    
    me.listeners = new Array();
//    me.ufxtractUrl = 'http://localhost/BacknetworkLab/ufXtract/';
//    me.proxyUrl = 'http://localhost:49648/uftest/proxies/proxy.aspx';
    
    me.ufxtractUrl = 'http://lab.backnetwork.com/ufXtract/';
    me.proxyUrl = 'http://www.ufxtract.com/proxies/proxy.aspx';
      
}

UtilitiesBase.prototype.addScript = function( url ) {
	var script = document.createElement("script");
	script.setAttribute("type", "text/javascript");
	
	if( url.indexOf('?') > -1)
		url += '&';
	else
		url += '?';
		
	url += 'rand=' + Math.random();
	script.setAttribute("src", url);
	document.getElementsByTagName('head')[0].appendChild(script);
	return script;
}
    
UtilitiesBase.prototype.insertAfter = function (parent, node, referenceNode) {
	parent.insertBefore(node, referenceNode.nextSibling);
}
    
UtilitiesBase.prototype.getElementsByClassName = function (className) {
    var children = document.getElementsByTagName("*") || document.all;
    var elements = new Array;
    for (var i = 0; i < children.length; i++) {
        var child = children[i];
        var classNames = child.className.split(" ");
        for (var j = 0; j < classNames.length; j++) {
            if (classNames[j] == className) {
                elements.push(child);
                break;
            }
        }
    }
    return elements;
}

    
UtilitiesBase.prototype.addEvent = function( elm, evType, fn, useCapture ) {
	// Updated version which captures passed events 
	if (elm.AddEventListener) 
	{ 
		elm.AddEventListener(evType, fn, useCapture); 
		return true; 
	} else if (elm.attachEvent) { 
		var r = elm.attachEvent('on' + evType, fn);
		this.listeners[this.listeners.length] = [ elm, evType, fn ];
		return r; 
	} else {
		var xEventFn = elm['on' + evType];
		if (typeof elm['on' + evType] != 'function') 
		{
			elm['on' + evType] = fn;
		} else {
			elm['on' + evType] = function(e) { xEventFn(e); fn(e); };
		}
	}
}

UtilitiesBase.prototype.unload = function(){
	// page unload event which removes circular references
	// that may cause memory leaks in IE 5/6
//	if( window.attachEvent ){
//	    for (var i = 0; i < this.listeners.length; i++) {
//		    this.listeners[i][0].detachEvent( 'on' + this.listeners[i][1], this.listeners[i][2] );
//	    }
//	}
}

UtilitiesBase.prototype.stopPropagation = function(e){
	// Use to close down unwanted event propagation/bubbling
	if( window.event ) {
	    window.event.cancelBubble = true;
	}else if ( typeof e != 'undefined' ) {
	    e.stopPropagation();
	}
}


UtilitiesBase.prototype.findEventSource = function(e) {
    if (typeof e == 'undefined')
        var e = window.event;

    var source;
    if (typeof e.target != 'undefined')
    {
        source = e.target;
    } else if (typeof e.srcElement != 'undefined') {
        source = e.srcElement;
    } else {
        return true;
    }

    if (source.nodeType == 3)
        source = source.parentNode;

    return source;
}











function TestRunner( objectRef )
{
    var me;
    if (this.constructor == TestRunner){
        me = this;
    }else{
        me = arguments[arguments.length-1];
    }
    
    UtilitiesBase(me);
    me.objectRef = objectRef;
    
    me.domLoaded = false;
    me.fixtureJSONLoaded = false;
    me.interfaceBuilt = false;
    
    me.parsers = new Array();
    me.pages = new Array();
    
    // Load testfixture while DOM builds
    // This is just for a single page at the moment
    me.pages[0] = new Page( me.objectRef, 0 );
    me.pages[0].getFixture( me.objectRef + ".pages[0].loadFixture" );
 
    me.load = function() {
        me.domLoaded = true;
    }
    
    
    // Add parser interface objects 
    me.addParserInterface = function(parserInterface){
        parserInterface.parent = me;
        parserInterface.parentObjectName = me.objectRef;
        parserInterface.parserNumber = me.parsers.length;
        me.parsers[me.parsers.length] = parserInterface;
    }
    
    
    // Fixture - start
    // ------------------------------------------------------------------------------
    
    // Checks key events to open test options form
    me.altKeyCheck = function(e) {
        var keyID = (window.event) ? event.keyCode : e.keyCode;
        var altKey = (window.event) ? event.altKey : e.altKey;
        var ctrlKey = (window.event) ? event.ctrlKey : e.ctrlKey;
        var shiftKey = (window.event) ? event.shiftKey : e.shiftKey;

        // keyID == 0 for Mac Firefox
        if(((keyID == 88)||(keyID == 900))&&(altKey == true)) // Alt X
        {
            me.displayTestOptionForm();
        }else if( ctrlKey && shiftKey && (keyID == 88)||(keyID == 900) ){ // Shift Ctrl X
            me.displayTestOptionForm();
        }
    }
    
    // Displays test options form
    me.displayTestOptionForm = function() {
        if( document.getElementById('testOptionForm') ) {
            var testOptionForm = document.getElementById('testOptionForm');
            testOptionForm.style.display = 'block';
        }
        else
        {
            if( me.domLoaded && me.fixtureJSONLoaded ) {
                me.buildTestOptionForm(); 
                me.buildResultsPanel();
                me.buildJSONPanel();
                // Recall function
                me.displayTestOptionForm();
            }
        }
    }
    
    // Hide test options form
    me.hideTestOptionForm = function() {
        if( document.getElementById('testOptionForm')) {
            var testOptionForm = document.getElementById('testOptionForm');
            testOptionForm.style.display = 'none';
        }
    }
    
    // Loads JavaScript modules for parsers
    me.loadJSFile = function() {
    	for (var i = 0; i < me.parsers.length; i++) {
		    if( me.parsers[i].supportsFormat( me.pages[0].testFixture.format ) ){
		    	for (var j = 0; j < me.parsers[i].externalJavaScript.length; j++) {
		    		if( me.parsers[i].externalJavaScript[j][0].toLowerCase() == me.pages[0].testFixture.format.toLowerCase() )
		    			this.addScript( me.parsers[i].externalJavaScript[j][1] );
				}	
		    }
		}
    }
    
    
    // Builds test options form using DOM inserts
    me.buildTestOptionForm = function() {
    
    
           
        // Creates and appends Test Runner form
		var div = document.createElement('div');
		div.setAttribute( 'id', 'testOptionForm'  );
		div.style.display = 'none';
		
		var img = document.createElement('img');
		img.setAttribute( 'alt', 'Microformats test runner' );
		img.setAttribute( 'title', 'Microformats test runner' );
		img.setAttribute( 'id', 'testOptionFormLogo' );
		img.setAttribute( 'src', 'http://ufxtract.com/testrunner/images/testrunner.gif' );
		
		var p1 = document.createElement('p');
		p1.appendChild( document.createTextNode('This tool run’s the microformats parser test described in this page and return’s a pass or fail for each test. You can choose which parser’s to test and view the JSON output of each.'));
	    
	    var p2 = document.createElement('p');
		p2.appendChild( document.createTextNode('The available parser’s for ' + me.pages[0].testFixture.format));
		
		var imgClose = document.createElement('img');
		imgClose.setAttribute( 'alt', 'Close' );
		imgClose.setAttribute( 'title', 'Close' );
		imgClose.setAttribute( 'id', 'testOptionFormCloseButton' );
		imgClose.setAttribute( 'src', 'http://ufxtract.com/testrunner/images/close.gif' );
		me.addEvent( imgClose, 'click', me.hideTestOptionForm, false );
		
		var a = document.createElement('a');
		a.setAttribute( 'id', 'testOptionFormSiteLink' );
		a.setAttribute( 'href', 'http://ufxtract.com/testrunner/' );
		a.appendChild( document.createTextNode('http://ufxtract.com/testrunner/'));
	    
	    div.appendChild(img);
		div.appendChild(p1);
		div.appendChild(p2);
		
		
		var ul = document.createElement('ul');
		ul.setAttribute( 'id', 'testOptionFormParserList' );
		div.appendChild(ul);
		
		for (var i = 0; i < me.parsers.length; i++) {
		    if( me.parsers[i].supportsFormat( me.pages[0].testFixture.format ) )
		        me.addParserChoice( me.parsers[i], i, ul );
		}
		
		var submitinput = document.createElement('input');
		submitinput.setAttribute( 'id', 'testOptionFormSubmit' );
		submitinput.setAttribute( 'name', 'testOptionFormSubmit' );
		submitinput.setAttribute( 'type', 'button' );
		submitinput.setAttribute( 'value', 'Run' );
		me.addEvent( submitinput, 'click', me.run, false );

		div.appendChild(submitinput);
		div.appendChild(imgClose);
		div.appendChild(a);
		document.body.appendChild(div);
		
		Drag.init(div, div);
		div.style.top = 200 + 'px';
		div.style.left  = 200 + 'px';
										
    } 
    
    // Builds the parser choice checkboxs in a list
    me.addParserChoice = function( parserInterface, number, parentNode ){
        var checkbox = document.createElement('input');
		checkbox.setAttribute( 'id', "parser" + number );
		checkbox.setAttribute( 'type', 'checkbox' );
		checkbox.setAttribute( 'checked', 'checked' );
		var span = document.createElement('span');
		span.appendChild( document.createTextNode(parserInterface.name) );
        var li = document.createElement('li');
        li.appendChild(checkbox);
        li.appendChild(span);
        parentNode.appendChild(li);
    }
    
    
    // Displays results panel
    me.displayResultsPanel = function() {
        me.hideTestOptionForm();
        if( document.getElementById('resultsPanel')) {
            var resultsPanel = document.getElementById('resultsPanel');
            resultsPanel.style.display = 'block';
        }
    }
    
    // Hide results panel
    me.hideResultsPanel = function() {
        if( document.getElementById('resultsPanel')) {
            var resultsPanel = document.getElementById('resultsPanel');
            resultsPanel.style.display = 'none';
        }
    }
    
    // Builds results panel form using DOM inserts
    me.buildResultsPanel = function() {
           
        // Creates and appends Test Runner form
		var div = document.createElement('div');
		div.setAttribute( 'id', 'resultsPanel'  );
		div.style.display = 'none';
		
		var img = document.createElement('img');
		img.setAttribute( 'alt', 'Microformats test runner' );
		img.setAttribute( 'title', 'Microformats test runner' );
		img.setAttribute( 'id', 'resultsPanelLogo' );
		img.setAttribute( 'src', 'http://ufxtract.com/testrunner/images/testrunner.gif' );
		
		var imgClose = document.createElement('img');
		imgClose.setAttribute( 'alt', 'Close' );
		imgClose.setAttribute( 'title', 'Close' );
		imgClose.setAttribute( 'id', 'resultsPanelCloseButton' );
		imgClose.setAttribute( 'src', 'http://ufxtract.com/testrunner/images/close.gif' );
		me.addEvent( imgClose, 'click', me.hideResultsPanel, false );
		div.appendChild(img);
		 
		var p1 = document.createElement('p');
		var strong = document.createElement('strong');
		strong.appendChild( document.createTextNode(me.pages[0].testFixture.summary));
		p1.appendChild( strong );
		p1.appendChild( document.createElement('br') );
		p1.appendChild( document.createTextNode(me.pages[0].testFixture.description));
		div.appendChild(p1);
		
		var a = document.createElement('a');
		a.setAttribute( 'id', 'resultsPanelSiteLink' );
		a.setAttribute( 'href', 'http://ufxtract.com/testrunner/' );
		a.appendChild( document.createTextNode('http://ufxtract.com/testrunner/'));

		var divScroll = document.createElement('div');
		divScroll.setAttribute( 'id', 'resultsPanelScrollTable' );
		div.appendChild(divScroll);
		
		var table = document.createElement('table');
		table.setAttribute( 'id', 'resultsPanelResultsTable' );
		table.setAttribute( 'cellpadding', '0' );
		table.setAttribute( 'cellspacing', '0' );
		var thead = document.createElement('thead');
		var tbody = document.createElement('tbody');
        divScroll.appendChild(table);
        table.appendChild(thead);
        table.appendChild(tbody);
        
        var tr = document.createElement('tr');
        me.addHeaderCell( tr, ' ', '', '' );
        me.addHeaderCell( tr, 'Test description', '', '' );
        // Add parser cells    
        for (var i = 0; i < me.parsers.length; i++) {
            me.addHeaderCell( tr, me.parsers[i].name, 'parserName', '');
		} 
        thead.appendChild(tr);
        
        
        for (var i = 0; i < me.pages[0].testFixture.assert.length; i++) {
		    me.addAssertRow(tbody, me.pages[0].testFixture.assert[i], i )
		}
		
		me.addEvent( divScroll, 'mousedown', me.stopPropagation, false );
        
		var submitJSON = document.createElement('input');
		submitJSON.setAttribute( 'id', 'resultsPanelOpenJSON' );
		submitJSON.setAttribute( 'name', 'resultsPanelOpenJSON' );
		submitJSON.setAttribute( 'type', 'button' );
		submitJSON.setAttribute( 'value', 'JSON/HTML' );
		me.addEvent( submitJSON, 'click', me.displayJSONPanel, false );
		
//		var submitHTML = document.createElement('input');
//		submitHTML.setAttribute( 'id', 'resultsPanelOpenHTML' );
//		submitHTML.setAttribute( 'name', 'resultsPanelOpenHTML' );
//		submitHTML.setAttribute( 'type', 'button' );
//		submitHTML.setAttribute( 'value', 'HTML' );
//		me.addEvent( submitHTML, 'click', me.displayJSONPanel, false );

        div.appendChild(submitJSON);
		//div.appendChild(submitHTML);
		div.appendChild(imgClose);
		div.appendChild(a);
		document.body.appendChild(div);
		
		Drag.init(div, div);
		div.style.top = 200 + 'px';
		div.style.left  = 200 + 'px';
										
    } 
    
    
    me.addAssertRow = function( tbody, assert, number ){
        var tr = document.createElement('tr');
        me.addCell( tr, number + 1, 'assertNumber', '' );
        me.addCell( tr, assert.comment, 'assertComment', '' );
        
        // Add parser cells    
        for (var i = 0; i < me.parsers.length; i++) {
            me.addCell( tr, '-', 'parserTestResults', '');
		} 
        
        tbody.appendChild( tr );
    }
    
    me.addCell = function( row, text, className, id ){
        var td = document.createElement('td');
        td.appendChild( document.createTextNode( text ));
        if( className != '' )
            td.className = className;
        if( id != '' )
            td.setAttribute( 'id', id );
        row.appendChild( td );     
    }
    
    me.addHeaderCell = function( row, text, className, id ){
        var th = document.createElement('th');
        th.appendChild( document.createTextNode( text ));
        if( className != '' )
            th.className = className;
        if( id != '' )
            th.setAttribute( 'id', id );
        row.appendChild( th );     
    }
    
    me.addTestResultImage = function( element, testResult ) {
        var img = document.createElement('img');
		img.setAttribute( 'alt', testResult );
		img.setAttribute( 'title', testResult );
		img.setAttribute( 'src', 'http://ufxtract.com/testrunner/images/' + testResult + '.gif' );
		element.innerHTML = '';
		element.appendChild( img );
    }
    
    
    // ----------------------------------------------------------------------
    
    
    me.run = function(){
        for (var i = 0; i < me.parsers.length; i++) {
            if( document.getElementById( "parser" + i ) ) {
                var checkbox = document.getElementById( "parser" + i );
                if( checkbox.checked ) {
                    me.parsers[i].getJSON( me.pages[0].testFixture, 0 );
                    me.parsers[i].selected = true;
		}
            } 
        } 
    }
    
    
    // Collects incoming results and displays them
    me.collectResults = function( results, parserNumber ){
  
        me.displayResultsPanel();
        var table = document.getElementById('resultsPanelResultsTable');
        var tbody = table.getElementsByTagName("tbody")[0];
 
        for (var i = 0; i < results.length; i++) {
            var cell = tbody.rows[i].cells[2+ parserNumber];
            me.addTestResultImage( cell, results[i].testStatus );
           
        }
    }
    
    
  
    me.parserUfJSONLoaded = function( parserNumber ) {
        var parser = me.parsers[parserNumber];
        
    }
    
    
    
        // Displays results panel
    me.displayJSONPanel = function() {
        me.hideTestOptionForm();
        if( document.getElementById('JSONPanel')) {
            var resultsPanel = document.getElementById('JSONPanel');
            resultsPanel.style.display = 'block';
            me.sitchJSONCodeDisplay();
        }
    }
    
    // Hide results panel
    me.hideJSONPanel = function() {
        if( document.getElementById('JSONPanel')) {
            var resultsPanel = document.getElementById('JSONPanel');
            resultsPanel.style.display = 'none';
        }
    }
    
    
       
    // Builds results panel form using DOM inserts
    me.buildJSONPanel = function() {
           
        // Creates and appends Test Runner form
		var div = document.createElement('div');
		div.setAttribute( 'id', 'JSONPanel'  );
		div.style.display = 'none';
		
		var img = document.createElement('img');
		img.setAttribute( 'alt', 'Microformats test runner' );
		img.setAttribute( 'title', 'Microformats test runner' );
		img.setAttribute( 'id', 'JSONPanelLogo' );
		img.setAttribute( 'src', 'http://ufxtract.com/testrunner/images/testrunner.gif' );
		
		var imgClose = document.createElement('img');
		imgClose.setAttribute( 'alt', 'Close' );
		imgClose.setAttribute( 'title', 'Close' );
		imgClose.setAttribute( 'id', 'JSONPanelCloseButton' );
		imgClose.setAttribute( 'src', 'http://ufxtract.com/testrunner/images/close.gif' );
		me.addEvent( imgClose, 'click', me.hideJSONPanel, false );
		div.appendChild(img);
		 
		var p1 = document.createElement('p'); 
		p1.appendChild( document.createTextNode('Select a parser: '));
		var select = document.createElement('select');
		select.setAttribute( 'id', 'JSONPanelSelect' );
		
		for (var i = 0; i < me.parsers.length; i++) {
		    select.options[select.options.length] = new Option(me.parsers[i].name, i, false, false);
		}
		select.options[select.options.length] = new Option("HTML", "html", false, false);
		
		me.addEvent( select, 'change', me.sitchJSONCodeDisplay, false );
		me.addEvent( select, 'mousedown', me.stopPropagation, false );
		
		p1.appendChild(select);
		div.appendChild(p1);
		
		var a = document.createElement('a');
		a.setAttribute( 'id', 'JSONPanelSiteLink' );
		a.setAttribute( 'href', 'http://ufxtract.com/testrunner/' );
		a.appendChild( document.createTextNode('http://ufxtract.com/testrunner/'));

		var divCodeBox = document.createElement('textarea');
		divCodeBox.setAttribute( 'id', 'JSONPanelCodeBox' );
		div.appendChild(divCodeBox);
		me.addEvent( divCodeBox, 'mousedown', me.stopPropagation, false );
		
		div.appendChild(imgClose);
		div.appendChild(a);
		document.body.appendChild(div);
		
		Drag.init(div, div);
		div.style.top = 200 + 'px';
		div.style.left  = 200 + 'px';
										
    } 
    
    // Converts JSON into a string and then formats for viewing
    me.loadPrittyPrinter = function( parserNumber) {
        if( document.getElementById('JSONPanelCodeBox') ) {
            var codeBox = document.getElementById('JSONPanelCodeBox');
            var uf = Object.toJSON(me.parsers[parserNumber].uf);
            codeBox.value = js_beautify( uf, 4, ' ');
       }
    }
    
    me.sitchJSONCodeDisplay = function() {
         if( document.getElementById('JSONPanelSelect') ) {
            var select = document.getElementById('JSONPanelSelect');
            if( select.options[select.options.selectedIndex].value == "html" ) {
                var codeBox = document.getElementById('JSONPanelCodeBox');
                var ufhtml = document.getElementById('uf');
                codeBox.value = ufhtml.innerHTML;
            }else {
                me.loadPrittyPrinter( select.options[select.options.selectedIndex].value );
            }
         }
    }
    
    
    //------------------------------------------


	// dom-drag.js
	// 09.25.2001
	// www.youngpup.net
	// 10.28.2001 - fixed minor bug where events
	// sometimes fired off the handle, not the root.
	var Drag = {

		obj : null,

		init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
		{
			o.onmousedown    = Drag.start;

			o.hmode            = bSwapHorzRef ? false : true ;
			o.vmode            = bSwapVertRef ? false : true ;

			o.root = oRoot && oRoot != null ? oRoot : o ;

			if (o.hmode  && isNaN(parseInt(o.root.style.left  ))) o.root.style.left   = "0px";
			if (o.vmode  && isNaN(parseInt(o.root.style.top   ))) o.root.style.top    = "0px";
			if (!o.hmode && isNaN(parseInt(o.root.style.right ))) o.root.style.right  = "0px";
			if (!o.vmode && isNaN(parseInt(o.root.style.bottom))) o.root.style.bottom = "0px";

			o.minX    = typeof minX != 'undefined' ? minX : null;
			o.minY    = typeof minY != 'undefined' ? minY : null;
			o.maxX    = typeof maxX != 'undefined' ? maxX : null;
			o.maxY    = typeof maxY != 'undefined' ? maxY : null;

			o.xMapper = fXMapper ? fXMapper : null;
			o.yMapper = fYMapper ? fYMapper : null;

			o.root.onDragStart    = new Function();
			o.root.onDragEnd    = new Function();
			o.root.onDrag        = new Function();
		},

		start : function(e)
		{
			var o = Drag.obj = this;
			e = Drag.fixE(e);
			var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
			var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
			o.root.onDragStart(x, y);

			o.lastMouseX    = e.clientX;
			o.lastMouseY    = e.clientY;

			if (o.hmode) {
				if (o.minX != null)    o.minMouseX    = e.clientX - x + o.minX;
				if (o.maxX != null)    o.maxMouseX    = o.minMouseX + o.maxX - o.minX;
			} else {
				if (o.minX != null) o.maxMouseX = -o.minX + e.clientX + x;
				if (o.maxX != null) o.minMouseX = -o.maxX + e.clientX + x;
			}

			if (o.vmode) {
				if (o.minY != null)    o.minMouseY    = e.clientY - y + o.minY;
				if (o.maxY != null)    o.maxMouseY    = o.minMouseY + o.maxY - o.minY;
			} else {
				if (o.minY != null) o.maxMouseY = -o.minY + e.clientY + y;
				if (o.maxY != null) o.minMouseY = -o.maxY + e.clientY + y;
			}

			document.onmousemove = Drag.drag;
			document.onmouseup = Drag.end;

			return false;
		},

		drag : function(e)
		{
			e = Drag.fixE(e);
			var o = Drag.obj;

			var ey    = e.clientY;
			var ex    = e.clientX;
			var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom);
			var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right );
			var nx, ny;

			if (o.minX != null) ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);
			if (o.maxX != null) ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);
			if (o.minY != null) ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);
			if (o.maxY != null) ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);

			nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
			ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));

			if (o.xMapper)        nx = o.xMapper(y)
			else if (o.yMapper)    ny = o.yMapper(x)

			Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
			Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
			Drag.obj.lastMouseX    = ex;
			Drag.obj.lastMouseY    = ey;

			Drag.obj.root.onDrag(nx, ny);
			return false;
		},

		end : function()
		{
			document.onmousemove = null;
			document.onmouseup   = null;
			Drag.obj.root.onDragEnd(    parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"]), 
										parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"]));
			Drag.obj = null;
		},

		fixE : function(e)
		{
			if (typeof e == 'undefined') e = window.event;
			if (typeof e.layerX == 'undefined') e.layerX = e.offsetX;
			if (typeof e.layerY == 'undefined') e.layerY = e.offsetY;
			return e;
		}
	};
    

	me.addEvent( window, 'load', me.load, false );
	me.addEvent(document, 'keydown', me.altKeyCheck, false);
	me.addEvent( window, 'unload', me.unload, false );
       
}
Inherit(UtilitiesBase, TestRunner);





function ParserInterface() {
    var me;
    if (this.constructor == ParserInterface){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    UtilitiesBase(me);
    
    me.parent;
    me.parentObjectName;
    me.parserNumber;
    
    me.name;
    me.formats = new Array();
    me.externalJavaScript = new Array();
    me.serviceUrl;
    me.supportsPJSON = true;
    me.supportsFragment = true;
    
    me.ufUrl;
    me.ufUrlNotEscaped;
    me.uf = new Object();
    me.ufRaw = new Object();
    me.results = new Array();
   
    me.reset = function(){
        me.ufUrl = null;
        me.ufUrlNotEscaped = null;
        me.uf = null;
        me.results = new Array();
        me.testFixture = null;
        me.pageNumber = null;
    }
    
    // Checks for supports of a given format
    me.supportsFormat = function( format ){
        var found = false;
        for (var i = 0; i < me.formats.length; i++) {
            if(String(me.formats[i]).toLowerCase() == format.toLowerCase())
                found = true;
        }
        return found;
    }
    
    
    // Testers
    //--------------------------------------------------------------------------------
    
    
    // Runs the test fixture using this parser
    me.run = function(){
       
       me.results = new Array();
       
       for (var i = 0; i < me.testFixture.assert.length; i++) {
            
            var test = me.testFixture.assert[i].test;
            var result = me.testFixture.assert[i].result;
            var comment = me.testFixture.assert[i].comment;
            var testStatus = "none";
            
            if( result.indexOf("IsEqualTo(") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.isEqualTo( test, result, comment )); 
            } 
            else if( result.indexOf("IsEqualToCaseInsensitive(") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.isEqualToCaseInsensitive( test, result, comment )); 
            } 
            else if( result.indexOf("IsEqualToISODate(") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.isEqualToISODate( test, result, comment )); 
            } 
            else if( result.indexOf("IsEqualToGeo(") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.isEqualToGeo( test, result, comment )); 
            } 
            else if( result.indexOf("IsEqualToPhoneNumber(") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.isEqualToPhoneNumber( test, result, comment )); 
            }     
            else if( result.indexOf("IsTrue()") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.isTrue( test, result, comment )); 
            }
            else if( result.indexOf("IsFalse()") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.isFalse( test, result, comment ));  
            }
            else if( result.indexOf("HasProperty(") > -1) {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], me.HasProperty( test, result, comment ));  
            }
            else {
                me.results[me.results.length] = new Result( me.pageNumber, i, me.testFixture.assert[i], 'unnone');
            } 
        } 
        
        // Call parent object function on complete
        if( me.parent.constructor == TestRunner )
            me.parent.collectResults( me.results, me.parserNumber );
        
    }
    
    
    
    // The "isEqualTo" tester
    me.isEqualTo = function( test, result, comment ) {
        resultValue = result.substring(11, result.length-2)
        testValue = String(me.getNodeVaue( test ) );
        if( testValue != '' && resultValue == testValue)
            return "passed";
        else
            return "failed";
    }

    // The "isEqualToCaseInsensitive" tester
    me.isEqualToCaseInsensitive = function( test, result, comment ) {
        resultValue = result.substring(26, result.length-2)
        testValue = String(me.getNodeVaue( test ) );
        if( testValue != '' && resultValue.toLowerCase() == testValue.toLowerCase())
            return "passed";
        else
            return "failed";
    }

   // The "isEqualToISODate" tester
    me.isEqualToISODate = function( test, result, comment ) {
		output = "failed"
        resultValue = String(result.substring(18, result.length-2));
        testValue = String(me.getNodeVaue( test ) );
        if( testValue != '' && resultValue.toLowerCase() == testValue.toLowerCase()) {
		output = "passed";
        }else{
            if( CompareISODates( testValue, resultValue ) )
               output = "passed";
		}
        return output;
    }
    
   // The "isEqualToGeo" tester
    me.isEqualToGeo = function( test, result, comment ) {
		output = "failed"
        resultValue = String(result.substring(14, result.length-2));
        testValue = String(me.getNodeVaue( test ) );
        if( testValue != '' && resultValue.toLowerCase() == testValue.toLowerCase()) {
		output = "passed";
        }else{
            if( CompareRFC2426Geo( testValue, resultValue ) )
               output = "passed";
		}
        return output;
    }

    
   	// The "isEqualToPhoneNumber" tester
    me.isEqualToPhoneNumber = function( test, result, comment ) {
		output = "failed"
        resultValue = result.substring(22, result.length-2)
        testValue = String(me.getNodeVaue( test ) );
	
        if( testValue != '' && resultValue.toLowerCase() == testValue.toLowerCase()) {
            output = "passed";
        }else{
            if( ComparePhoneNumbers( testValue, resultValue ) )
               output = "passed";
		}
        return output;
    }

    
    // The "isTrue" tester
    me.isTrue = function( test, result, comment ) {
        testValue = me.getNodeVaue( test );
        if( typeof(testValue)!= 'undefined' && String(testValue.toLowerCase()) == 'true' )
            return "passed";
        else
            return "failed";
    }
    
    // The "isFalse" tester
    me.isFalse = function( test, result, comment ) {
        testValue = me.getNodeVaue( test );
        if( typeof(testValue)!= 'undefined' && String(testValue.toLowerCase()) == 'false')
            return "passed";
        else
            return "failed";
    }
    
    // The "HasProperty" tester
    me.HasProperty = function( test, result, comment ) {
        
        var testValue = me.getNodeVaue( test );
        var resultValue =  result.substring(12, result.length-1).toLowerCase();
        var output = "failed";

        
        var nodeHasProperty = true;
        if( testValue == 'undefined' || testValue == null)
            nodeHasProperty = false;    
            
        if( nodeHasProperty == true && resultValue == "true")
            output = "passed";
            
        if ( nodeHasProperty == false && resultValue == 'false')
            output = "passed";   
        
        return output;
    }
 
    // A helper function to finds a value of a given JSON property
    me.getNodeVaue = function( test )
    {
        // Gets a value from a JSON object
        // vcard[0].url[0]
        var output = null;
        try{
            var currentOject = me.uf;
            var arrayDots = test.split(".");
            for (var i = 0; i < arrayDots.length; i++) {
                if( arrayDots[i].indexOf('[') > -1 )
                {
                    // Reconstructs and adds access to array of objects
                    var arrayAB = arrayDots[i].split("[");
                    var arrayName = arrayAB[0];
                    var arrayPosition = Number( arrayAB[1].substring(0,arrayAB[1].length-1) );
		            
		            if( currentOject[arrayName] != null || currentOject[arrayName] != 'undefined' ) {
		                if( currentOject[arrayName][arrayPosition] != null || currentOject[arrayName][arrayPosition] != 'undefined' )
                   	        currentOject = currentOject[arrayName][arrayPosition];
                   	           
                    }
                    else {
                        currentObject = null; 
                    }
                        
                }
                else
                {
                    // Adds access to a property using property array ["given-name"]
	                if( currentOject[arrayDots[i]] != null || currentOject[arrayDots[i]] != 'undefined'   )
                	    currentOject = currentOject[arrayDots[i]];    
                }
            }
            output = currentOject;
        }catch(err){
            // Add error capture
            output = null;
        }
        return output;
    }
    
    //--------------------------------------------------------------------------------
}
Inherit(UtilitiesBase, ParserInterface);


// Loads JSON from a parser API service
ParserInterface.prototype.getJSON = function( testFixture, pageNumber ){
    this.testFixture = testFixture;
    this.pageNumber = pageNumber;
    var thisDocument = '';
    if( !this.supportsPJSON || !this.supportsFragment  ) {
    
	    // Uses the proxy service to create fragment and PJSON support
	    thisDocument = escape(document.location.href);
        this.ufUrl = this.proxyUrl + "?serviceurl=[serviceurl]&fragmentid=[fragmentid]&url=[url]&format=[format]&callback=[callback]";
        this.ufUrl = this.ufUrl.replace("[serviceurl]", escape(this.serviceUrl));
        this.ufUrl = this.ufUrl.replace("[fragmentid]", 'uf');
        this.ufUrl = this.ufUrl.replace("[url]", escape(thisDocument));
        this.ufUrl = this.ufUrl.replace("[format]", testFixture.format.toLowerCase());
        this.ufUrl = this.ufUrl.replace("[callback]", this.parentObjectName + '.parsers[' + this.parserNumber + '].loadJSON');
	    this.addScript(this.ufUrl);
   
    } else {
    
        thisDocument = document.location.href + "#uf";
        this.ufUrl = this.serviceUrl;
        this.ufUrl = this.ufUrl.replace("[url]", escape(thisDocument));
        this.ufUrl = this.ufUrl.replace("[format]", testFixture.format.toLowerCase());
        this.ufUrl = this.ufUrl.replace("[callback]", this.parentObjectName + '.parsers[' + this.parserNumber + '].loadJSON');
	    this.addScript(this.ufUrl);
	    
	    this.ufUrlNotEscaped = this.serviceUrl.replace("[url]", thisDocument);
	    this.ufUrlNotEscaped = this.ufUrlNotEscaped.replace("[format]", testFixture.format.toLowerCase());
	    
	}
	
	this.selected = false;	    	    
}

// Callback function of loading JSON data from a parser API service
ParserInterface.prototype.loadJSON = function( data ){
    this.uf = data;
    this.ufRaw = data;
    this.run();
}




// Simple result storage object used by ParserInterface
function Result( pageNumber, assertNumber, assert, testStatus ) {
    var me;
    if (this.constructor == Result){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    me.pageNumber = pageNumber;
    me.assertNumber = assertNumber;
    me.assert = assert; // object with properties
    me.testStatus = testStatus;
}




function Page( parentObjectName, pageNumber ) {
    var me;
    if (this.constructor == Page){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    UtilitiesBase(me);
    
    me.parentObjectName = parentObjectName;
    me.pageNumber = pageNumber;
    me.testFixture;
    me.hasFixture = false;
    me.parsers = new Array();
    me.uf = new Array();
    me.results = new Array()
}
Inherit(UtilitiesBase, Page);


Page.prototype.loadFixture = function ( data ) {
	if(typeof(data['test-fixture'][0]) == "object")
	{ 
        this.testFixture = data['test-fixture'][0];
        this.hasFixture = true;
        microformatsTestRunner.fixtureJSONLoaded = true;
        microformatsTestRunner.loadJSFile();
    }
}

Page.prototype.getFixture = function() {
    url = this.ufxtractUrl;
    url += '?url=' + document.location.href;
    url += '&format=test-fixture';
    url += '&output=json';
	url += '&callback=' + this.parentObjectName + '.pages[' + this.pageNumber + '].loadFixture';
	this.addScript(url);
}


//------------------------------------------------------------------------

function Inherit( base, derived ){
	for ( var i in base.prototype )
		derived.prototype[i] = base.prototype[i];
}	






// Create object
var microformatsTestRunner = new TestRunner( "microformatsTestRunner");


// ufXtract
var ufxtract = new ParserInterface();
ufxtract.name = "ufXtract";
ufxtract.formats = ["hCalendar","hCard","hEntry","hResume","hReview","adr","geo","xfn","votelinks",,"rel-tag","rel-license"];
ufxtract.serviceUrl = "http: //ufxtract.com/api/default.aspx?url=[url]&format=[format]&output=json&callback=[callback]";
microformatsTestRunner.addParserInterface(ufxtract);


//------------------------------------------------------------------------

// Inherits from ParserInterface
function OptimusParserInterface() {
    var me;
    if (this.constructor == OptimusParserInterface){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    ParserInterface(me);
}
Inherit(ParserInterface, OptimusParserInterface);

// Override the base class function
OptimusParserInterface.prototype.loadJSON = function( data ){
    
     this.ufRaw = data;
    
    var ufName = '';
    var optimusName = '';
    if( this.testFixture.format.toLowerCase() == "hcard" )
	{	 
        ufName = "vcard";
	optimusName = "hcard";
	}
    else if( this.testFixture.format.toLowerCase() == "hcalendar" ) 
{
        ufName = "vevent";
	optimusName = "hcalendar";
}
    else
{
        ufName = this.testFixture.format.toLowerCase();
	optimusName = this.testFixture.format.toLowerCase();
}


if( Object.isArray( data[optimusName]) )
	this.uf[ufName] = data[optimusName];
else
	this.uf[ufName] = new Array(data[optimusName]);

this.uf["parser-information"] =  {"name": "Optimus"};

    this.run();

}

// Optimus
var optimus = new OptimusParserInterface();
optimus.name = "Optimus";
optimus.supportsFragment = false;
optimus.formats = ["hCalendar","hCard","hEntry","hResume","hReview","hListing","hAudio","xFolkentry","adr","geo","xfn","votelinks","rel-nofollow","rel-tag","rel-license"];
optimus.serviceUrl = "http://www.microformatique.com/optimus/?uri=[url]&format=json&filter=[format]";
microformatsTestRunner.addParserInterface(optimus);

//------------------------------------------------------------------------

// Inherits from ParserInterface
function hKitParserInterface() {
    var me;
    if (this.constructor == hKitParserInterface){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    ParserInterface(me);
}
Inherit(ParserInterface, hKitParserInterface);

// Override the base class function
hKitParserInterface.prototype.loadJSON = function( data ){
    
    this.ufRaw = data;
    var  JSONtext = String( Object.toJSON( data ) );
    JSONtext = '{ "vcard": ' + JSONtext + ', "parser-information" : {"name": "hKit"}}';
    this.uf = JSONtext.evalJSON();
    this.run();
}

// hkit (Does not yet support p-json)
var hkit = new hKitParserInterface();
hkit.name = "hkit";
hkit.supportsPJSON = false;
hkit.supportsFragment = false;
hkit.formats = ["hCard"];
hkit.serviceUrl = "http://tools.microformatic.com/query/json/hkit/[url]";
microformatsTestRunner.addParserInterface(hkit);

//------------------------------------------------------------------------

// Inherits from ParserInterface
function SumoParserInterface() {
    var me;
    if (this.constructor == SumoParserInterface){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    ParserInterface(me);
}
Inherit(ParserInterface, SumoParserInterface);


// Override the base class function
SumoParserInterface.prototype.getJSON = function( testFixture, pageNumber ){
    this.testFixture = testFixture;
    this.pageNumber = pageNumber;
    var data = new Object();
    var ufExample = document.getElementById('uf');
    
    if( this.testFixture.format.toLowerCase() == "hcard" ) {
        var hcards = HCard.discover( ufExample );
        data = { "vcard" : hcards, "parser-information" : {"name": "Sumo"} }
    }
   	if( this.testFixture.format.toLowerCase() == "hcalendar" ) {
        var hcards = HCalendar.discover( ufExample );
        data = { "vevent" : hcards, "parser-information" : {"name": "Sumo"} }
    }

    this.ufRaw = data;

    var  JSONtext = String( Object.toJSON( data ) );
    JSONtext = JSONtext.replace(/List/g, "");
    // RegExp taken from mootools - string.hyphenate
    JSONtext = JSONtext.replace(/\w[A-Z]/g, function(A){return(A.charAt(0)+"-"+A.charAt(1).toLowerCase());});
   
    
    this.uf = JSONtext.evalJSON();
    this.run();
}


// Sumo
var sumo = new SumoParserInterface();
sumo.name = "Sumo";
sumo.formats = ["hCard","hCalendar","hreview"];
sumo.externalJavaScript.push(new Array("hcard","http://ufxtract.com/testrunner/javascript/sumo/hcard.js"));
sumo.externalJavaScript.push(new Array("hcalendar","http://ufxtract.com/testrunner/javascript/sumo/hcalendar.js"));
sumo.externalJavaScript.push(new Array("hreview","http://ufxtract.com/testrunner/javascript/sumo/hreview.js"));

microformatsTestRunner.addParserInterface(sumo);  


//------------------------------------------------------------------------

// Inherits from ParserInterface
function OperatorParserInterface() {
    var me;
    if (this.constructor == OperatorParserInterface){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    ParserInterface(me);
}
Inherit(ParserInterface, OperatorParserInterface);


// Override the base class function
OperatorParserInterface.prototype.getJSON = function( testFixture, pageNumber ){
    this.testFixture = testFixture;
    this.pageNumber = pageNumber;
    this.ufRaw = new Object();
    
    var ufName = '';
    if( this.testFixture.format.toLowerCase() == "hcard" ) 
        ufName = "vcard";
    else if( this.testFixture.format.toLowerCase() == "hcalendar" ) 
        ufName = "vevent";
    else
        ufName = this.testFixture.format.toLowerCase();
    
    
    // Simple hack to add Operator
    var ufExample = document.getElementById('uf');
    var ufElements = ufExample.getElementsByClassName(ufName);
    var ufs = new Array();
    
    for (var i = 0; i < ufElements.length; i++) {
    	var uf;
    	if( ufName == "vcard"  )
        	uf = new hCard( ufElements[i] );
        if( ufName == "vevent"  )
        	uf = new hCalendar( ufElements[i] );
       	if( ufName == "hresume"  )
        	uf = new hResume( ufElements[i] );
       	if( ufName == "geo"  )
        	uf = new geo( ufElements[i] );

        ufs.push( uf );
    }
    
    // Next line needs reworking
    //this.uf = { ufName : ufs, "parser-information" : {"name": "Operator"} }

    var  JSONtext = String( Object.toJSON( ufs ) );
    JSONtext = '{ "' + ufName + '": ' + JSONtext + ', "parser-information" : {"name": "Operator"} }';
    this.uf = JSONtext.evalJSON();


    this.ufRaw = ufs;
    
    this.run();
}


// Operator
var operator = new OperatorParserInterface();
operator.name = "Operator ";
operator.formats = ["hCard","hCalendar","hResume","xfn","geo","adr","hReview","hAtom"]; // Add correct support
operator.externalJavaScript.push(new Array("hresume","http://ufxtract.com/testrunner/javascript/operator/hresume.js"));
operator.externalJavaScript.push(new Array("xfn","http://ufxtract.com/testrunner/javascript/operator/xfn.js"));
operator.externalJavaScript.push(new Array("hreview","http://ufxtract.com/testrunner/javascript/operator/hreview.js"));
operator.externalJavaScript.push(new Array("hatom","http://ufxtract.com/testrunner/javascript/operator/hatom.js"));

microformatsTestRunner.addParserInterface(operator);




//------------------------------------------------------------------------

// Inherits from ParserInterface
function ShivParserInterface() {
    var me;
    if (this.constructor == ShivParserInterface) {
        me = this
    } else {
        me = arguments[arguments.length - 1]
    }
    ParserInterface(me);
}
Inherit(ParserInterface, ShivParserInterface);


// Override the base class function
ShivParserInterface.prototype.getJSON = function (testFixture, pageNumber) {
    this.testFixture = testFixture;
    this.pageNumber = pageNumber;
    this.ufRaw = new Object();

    var ufName = '';
    if (this.testFixture.format.toLowerCase() == "hcard")
        ufName = "vcard";
    else if (this.testFixture.format.toLowerCase() == "hcalendar")
        ufName = "vevent";
    else
        ufName = this.testFixture.format.toLowerCase();


    // Simple hack to add Shiv
    var ufExample = document.getElementById('uf');
    var ufElements = ufExample.getElementsByClassName(ufName);
    var ufs = new Array();

    for (var i = 0; i < ufElements.length; i++) {
        var uf;
        if (ufName == "vcard")
            uf = new hCard(ufElements[i]);
        if (ufName == "vevent")
            uf = new hCalendar(ufElements[i]);
        if (ufName == "hresume")
            uf = new hResume(ufElements[i]);
        if (ufName == "geo")
            uf = new geo(ufElements[i]);

        ufs.push(uf);
    }

    // Next line needs reworking
    //this.uf = { ufName : ufs, "parser-information" : {"name": "Operator"} }

    var JSONtext = String(Object.toJSON(ufs));
    JSONtext = '{ "' + ufName + '": ' + JSONtext + ', "parser-information" : {"name": "Microformats Shiv"} }';
    this.uf = JSONtext.evalJSON();


    this.ufRaw = ufs;

    this.run();
}


// Microformats Shiv
var operator = new ShivParserInterface();
shiv.name = "Microformats Shiv";
shiv.formats = ["hCard", "hCalendar", "geo", "adr"]; 

microformatsTestRunner.addParserInterface(shiv);


//------------------------------------------------------------------------

// Inherits from ParserInterface
function CognitionParserInterface() {
    var me;
    if (this.constructor == CognitionParserInterface){
        me = this
    }else{
        me = arguments[arguments.length-1]
    }
    ParserInterface(me);
}
Inherit(ParserInterface, CognitionParserInterface);

CognitionParserInterface.prototype.getJSON = function( testFixture, pageNumber ){
    this.testFixture = testFixture;
    this.pageNumber = pageNumber;
    var thisDocument = '';
    // Uses the proxy service to create fragment and PJSON support
    thisDocument = escape(document.location.href);

    this.ufUrl = this.proxyUrl + "?serviceurl=[serviceurl]&fragmentid=[fragmentid]&url=[url]&format=[format]&callback=[callback]&removehttp=true";
    this.ufUrl = this.ufUrl.replace("[serviceurl]", escape(this.serviceUrl));
    this.ufUrl = this.ufUrl.replace("[fragmentid]", 'uf');
    this.ufUrl = this.ufUrl.replace("[url]", escape(thisDocument));
    this.ufUrl = this.ufUrl.replace("[format]", "jcard");
    this.ufUrl = this.ufUrl.replace("[callback]", this.parentObjectName + '.parsers[' + this.parserNumber + '].loadJSON');

    this.addScript(this.ufUrl);
    this.selected = false;	    	    
}

// Override the base class function
CognitionParserInterface.prototype.loadJSON = function( data ){
    
    this.ufRaw = data;
    var  JSONtext = String( Object.toJSON( data ) );
    JSONtext = '{ "vcard": ' + JSONtext + '}';
    this.uf = JSONtext.evalJSON();
    this.run();
}

var cognition = new CognitionParserInterface();
cognition.name = "Cognition";
cognition.supportsPJSON = false;
cognition.supportsFragment = false;
cognition.formats = ["hCard"];
cognition.serviceUrl = "http://srv.buzzword.org.uk/jcard/[url]";
microformatsTestRunner.addParserInterface(cognition);









// ISO Date 
// -----------------------------------------------------------------------------

function ISODate() {
	this.dY;
	this.dM=-1; 
	this.dD=-1;
	this.z=false;
	this.tH;
	this.tM=-1;
	this.tS=-1;
	this.tD=-1;
	this.tzH;
	this.tzM=-1;
	this.tzPN='+';
	this.z = false;
	this.format = 'W3C' // W3C or RFC3339
	
	if(arguments[0])
		this.Parse( arguments[0] );
}

ISODate.prototype.Parse = function( dateString ) {
	
	var dateNormalised = '', parts;
	var datePart = '', timePart = '', timeZonePart = '';
	dateString = dateString.toUpperCase();

	
	// Break on 'T' divider or space
	if( dateString.indexOf('T') > -1 )
	{
		parts = dateString.split('T');		
		datePart = parts[0];
		timePart = parts[1];
		
		// Zulu UTC and time zone break
		if( timePart.indexOf('Z') > -1 || timePart.indexOf('+') > -1 || timePart.indexOf('-') > -1 )
		{
			var tzArray = timePart.split('Z');
			timePart = tzArray[0];
			timeZonePart = tzArray[1];
			this.z = true;
			
			// Timezone
			if( timePart.indexOf('+') > -1 || timePart.indexOf('-') > -1 )
			{
				var position = 0;
				if( timePart.indexOf('+') > -1 )
					position = timePart.indexOf('+');
				else
					position = timePart.indexOf('-');
				
				timeZonePart = timePart.substring(position,timePart.length);
				timePart = timePart.substring(0,position);
			}
		}
		
	}
	else
	{
		datePart = dateString;
	}
	
	if( datePart != '' ) {
		this.ParseDate( datePart );
		if( timePart != '' ){
			this.ParseTime( timePart );
			if( timeZonePart != '' ){
				this.ParseTimeZone( timeZonePart );
			}
		}
	}	
}


ISODate.prototype.ParseDate = function( dateString ) {
	var dateNormalised = '', parts;
	// YYYY-MM-DD ie 2008-05-01 and YYYYMMDD ie 20080501
	parts = dateString.match(/(\d\d\d\d)?-?(\d\d)?-?(\d\d)?/);
	if( parts[1] ) { this.dY = parts[1] };
	if( parts[2] ) { this.dM = parts[2] };
	if( parts[3] ) { this.dD = parts[3] };
}

ISODate.prototype.ParseTime = function( timeString ) {
	var timeNormalised = '';
	// Finds timezone HH:MM:SS and HHMMSS  ie 13:30:45, 133045 and 13:30:45.0135
	var parts = timeString.match(/(\d\d)?:?(\d\d)?:?(\d\d)?.?([0-9]+)?/);
	timeSegment = timeString;
	if( parts[1] ) { this.tH = parts[1] };
	if( parts[2] ) { this.tM = parts[2] };
	if( parts[3] ) { this.tS = parts[3] };
	if( parts[4] ) { this.tD = parts[4] };
}

ISODate.prototype.ParseTimeZone = function( timeString ) {
	var timeNormalised = '';
	// Finds timezone +HH:MM and +HHMM  ie +13:30 and +1330
	var parts = timeString.match(/([-+]{1})?(\d\d)?:?(\d\d)?/);
	if( parts[1] ) { this.tzPN = parts[1] };
	if( parts[2] ) { this.tzH = parts[2] };
	if( parts[3] ) { this.tzM = parts[3] };
}

// Returns datetime in W3C Note datetime profile or RFC 3339 profile
ISODate.prototype.toString = function() {
	if(this.format == 'W3C') {
		dsep = '-';
		tsep = ':';
	}
	if(this.format == 'RFC3339') {
		dsep = '';
		tsep = '';
	}
	
	var output='';
	if( typeof( this.dY ) != 'undefined' ){			
		output = this.dY;
		if( this.dM > 0 && this.dM < 13  ){ 
			output += dsep + this.dM;	
			if(this.dD > 0 && this.dD < 32  ){ 
				output += dsep + this.dD;
				// Time and can only be created with a full date
				if( typeof( this.tH ) != 'undefined' ){
					if( this.tH > -1 && this.tH < 25 ) {
						output += 'T' + this.tH
						if(  this.tM > -1 && this.tM < 61 ) {
							output += tsep + this.tM;
							if(  this.tS > -1 && this.tS < 61 ) {
								output += tsep + this.tS;
								if(  this.tD > -1 )
									output += '.' + this.tD; 
							}
						}
							// Time zone offset can only be created with a hour
							if(this.z) { output += 'Z' };
							if( typeof( this.tzH ) != 'undefined' ){
								if( this.tzH > -1 && this.tzH < 25 ) {
									output += this.tzPN + this.tzH
									if( this.tzM > -1 && this.tzM < 61 ) 
										output += tsep + this.tzM;
								}
							}
					}	
				}	
			}
		}	
	}
	return output;
}




function CompareISODates( date1, date2 ) {
	var iso1 = new ISODate( date1 ).toString().replace(/:00/g,"");
	var iso2 = new ISODate( date2 ).toString().replace(/:00/g,"");
	if( ( iso1 == '') && (iso2 == '') ){
		return false;
	}else{
		if( iso1 == iso2 )
			return true;
		else
			return false;
     }
}




function PhoneNumber() {
    this.raw = "";
    this.canonicalisation = "";	
	if(arguments[0])
		this.Parse( arguments[0] );
}

PhoneNumber.prototype.Reset = function(){
    this.raw = "";
    this.canonicalisation = "";
}



PhoneNumber.prototype.Parse = function( text ) {
	this.Reset();
    this.raw = text;
    text = text.replace(" ", "");
    var parts 
    parts = text.match(/[0-9\+]/g);
if( Object.isArray(parts))
{
	if( Object.isArray(parts))
	{
    	for (var i = 0; i < parts.length; i++) {
    		this.canonicalisation += parts[i];
    	}
	}
}
}

PhoneNumber.prototype.toString = function() {
	return this.canonicalisation;
}

function ComparePhoneNumbers( phoneNumber1, phoneNumber2 ) {
	var test1 = new PhoneNumber( phoneNumber1 ).toString();
	var test2 = new PhoneNumber( phoneNumber2 ).toString();
	if( ( test1 == '') && (test2 == '') ){
		return false;
	}else{
		if( test1 == test2 )
			return true;
		else
			return false;
     }
}



function RFC2426Geo() {
	
	this.latitude = 0;
	this.longitude = 0;
	
	if(arguments[0])
		this.Parse( arguments[0] );
}

RFC2426Geo.prototype.Parse = function( geo ) {
	
	this.Reset();
	if (geo.indexOf(";") > 0)
    {
    	parts = geo.split(';');
    	
    	// Make sure no more 6 decimal places
    	this.latitude = parseFloat(parts[0]).toFixed(6);
        this.longitude = parseFloat(parts[1]).toFixed(6);

    	// Remove trailing zero's
    	this.latitude = parseFloat(this.FormatNumber(String(this.latitude)));
        this.longitude = parseFloat(this.FormatNumber(String(this.longitude)));

        if (this.latitude > 90 || this.latitude < -90)
            this.latitude = 0;

        if (this.longitude > 180 || this.longitude < -180)
            this.longitude = 0;
            
    }
}

RFC2426Geo.prototype.Reset = function() {
	this.latitude = 0;
	this.longitude = 0;
}

// Returns location in RFC2426 geo format
RFC2426Geo.prototype.toString = function() {
	return this.latitude + ";" + this.longitude;
}

RFC2426Geo.prototype.FormatNumber = function( geo ) {

	var decPos = geo.indexOf(".")
	if (decPos > -1){
		first = geo.substring(0,decPos);
		second = geo.substring(decPos,geo.length);

		while (second.charAt(second.length-1) == "0")
			second = second.substring(0,second.length-1);

		if (second.length > 1)
			return first + second;
		else
			return first;

	}
	return geo;
}

function CompareRFC2426Geo( geo1, geo2 ) {
	var converted1 = new RFC2426Geo( geo1 ).toString();
	var converted2 = new RFC2426Geo( geo2 ).toString();
	if( ( converted1 == '') && (converted2 == '') ){
		return false;
	}else{
		if( converted1 == converted2 )
			return true;
		else
			return false;
     }
}






